// 牛牛规则判断
package service

import (
	"fmt"
	"math/rand"
	"poker21/model"
	"time"
)

const (
	POKER_JOKER_SMALL = 14 //小王牌[大小王点数统一10点]
	POKER_JOKER_BIG   = 15 //大王牌[大小王点数统一10点]
)
const (
	COW_0 int = iota //没牛
	COW_1            //牛1
	COW_2
	COW_3
	COW_4
	COW_5
	COW_6
	COW_7
	COW_8
	COW_9
	COW_10   //牛牛
	COW_4F   //四花牛
	COW_5F   //五花牛
	COW_BOMB //炸弹牛
	COW_5S   //五小牛
)

func GetCOWList() []int {
	return []int{COW_0, COW_1, COW_2, COW_3, COW_4, COW_5, COW_6, COW_7, COW_8, COW_9, COW_10, COW_4F, COW_5F, COW_BOMB, COW_5S} // 对应上面常量
}

func GetCOWOne(i int) int {
	list := GetCOWList()
	if i > len(list) {
		return list[len(list)-1]
	}
	return list[i]
}

// -----------可单独文件，可从数据库读取-begin
func (conf *ConfCardsIsNiu) NewConfCardsIsNiu() {
	//后期可以配置在数据库上,对应上[COW_0...COW_14]
	conf.ConfNIUData = []ConfNIUData{{"没牛", 0}, {"牛一", 1}, {"牛二", 1}, {"牛三", 1}, {"牛四", 1}, {"牛五", 1}, {"牛六", 1}, {"牛七", 1}, {"牛八", 2}, {"牛九", 2}, {"牛牛", 3}, {"四花牛", 4}, {"五花牛", 5}, {"炸弹牛", 6}, {"五小牛", 8}}
}

type ConfCardsIsNiu struct {
	ConfNIUData []ConfNIUData //下标就是牛几
}

// 获取牛几对应的倍率
func (conf *ConfCardsIsNiu) GetConfCardsIsNiu(index int) (ret ConfNIUData) {
	conf.NewConfCardsIsNiu()
	for k, v := range conf.ConfNIUData {
		if k == index {
			ret = v
		}
	}
	return
}

type ConfNIUData struct {
	Name string //名称
	Rate int    //倍率
}

//-----------可单独文件，可从数据库读取-end

// 庄家、玩家牌，从这个函数开始...
func CheckAllCards(s []model.CardsData) error {
	if len(s) != 5 {
		return nil //不够5张牌
	}

	var ccIsNiu ConfCardsIsNiu

	keyNiu := firstGtNiu(s)
	// 没牛-牛牛，11个数开始
	if keyNiu > 11 {
		ret := ccIsNiu.GetConfCardsIsNiu(keyNiu) //根据下标获取牛几+倍率
		fmt.Printf("keyNiu: %v, ret: %v \n", keyNiu, ret)
		return nil
	}

	cData := IsNiuNiu(s)
	isNiu := cData.IsNiu //是否有牛
	niuNo := cData.NiuNo
	fmt.Printf("s: %v, cData:%+v, keyNiu: %v \n", s, cData, keyNiu)
	if !isNiu {
		//没牛就比大小，比花色
		fmt.Printf("%v \n", "没牛就比大小，比花色")
	}

	switch niuNo {
	case 1, 2, 3, 4, 5, 6, 7, 8, 9:
		//牛1-牛6
	case 10:
	default:

	}
	return nil
}

// 先判断牌的最大牛
func firstGtNiu(s []model.CardsData) int {
	var conf ConfCardsIsNiu
	conf.NewConfCardsIsNiu()
	niuData := conf.ConfNIUData
	lennData := len(niuData)
	if lennData <= 11 {
		return 0 //没有比牛牛大的
	}

	is5s := IsFiveSmallCow(s) //五小牛
	if is5s {
		return lennData - 1
	}
	isBomb := IsBombCow(s) //炸弹牛
	if isBomb {
		return lennData - 2
	}
	is5f := IsFiveFlowerCow(s) //五花牛
	if is5f {
		return lennData - 3
	}
	is4f := IsFourFlowerCow(s) //四花牛
	if is4f {
		return lennData - 4
	}
	return 0
}

// 庄家ID(第一轮是随机庄，之后>=牛牛，最大牌坐庄)[i=玩家人数]
func GetRandIntn(i int) int {
	rand.Seed(time.Now().UnixNano())
	return rand.Intn(i)
}

type CowData struct {
	DealerID int               //坐庄ID
	NCards   []model.CardsData //3张组合的牛
	PCards   []model.CardsData //2张组合的牛几
	IsNiu    bool              //是否有牛
	NiuNo    int               //牛几对应下标
	Rate     int               //倍率
}

// 检查牛几，返回对应数据
func IsNiuNiu(s []model.CardsData) CowData {
	var cData CowData

	lens := len(s)
	if lens != 5 {
		return cData
	}

	var ccIsNiu ConfCardsIsNiu
	//5个数取3个数
	nCards, isNiu := _5Get3(s) //是否有牛
	if !isNiu || (len(nCards) == 0) {
		ret := ccIsNiu.GetConfCardsIsNiu(0) //根据下标获取没牛+倍率
		cData.Rate = ret.Rate
		// fmt.Printf("没牛 isNiu: %v, cData:%v \n", isNiu, cData)
		return cData //没牛且没数据
	}
	// fmt.Printf("-开始-s: %v \n", s)
	pCards := _5Cut3(s, nCards)
	// fmt.Printf("-结束-s: %v \n", s)
	if len(pCards) == 0 || len(pCards) != 2 {
		return cData
	}
	niuNo := CheckPCardsIsNiu(pCards)
	ret := ccIsNiu.GetConfCardsIsNiu(niuNo) //根据下标获取牛几+倍率

	cData.NCards = nCards
	cData.PCards = pCards
	cData.IsNiu = isNiu
	cData.NiuNo = niuNo
	cData.Rate = ret.Rate
	// fmt.Printf("isNiu: %v, ret: %v-- cData:%v \n", isNiu, ret, cData)
	return cData
}

// 10,J,Q,K,大小王返回10点，否则返回该值
func Cards10JQKJOKER(p int) (ret int) {
	if p <= 0 {
		return
	}
	switch p {
	case 10, 11, 12, 13, POKER_JOKER_SMALL, POKER_JOKER_BIG:
		ret = 10
	default:
		ret = p
	}
	return
}

// 5个数取3个数，判断是否有牛
func _5Get3(s []model.CardsData) (nCards []model.CardsData, isNiu bool) {
	lens := len(s)
	if lens != 5 {
		return nCards, isNiu
	}
	{
	for_i:
		for i := 0; i < lens-2; i++ {
			for j := i + 1; j < lens-1; j++ {
				for k := j + 1; k < lens; k++ {
					isNiu = false                                                                                         //是否有牛
					totalPoint := Cards10JQKJOKER(s[i].Point) + Cards10JQKJOKER(s[j].Point) + Cards10JQKJOKER(s[k].Point) //是否10的倍数
					if totalPoint%10 == 0 {
						nCards = []model.CardsData{} //初始化
						nCards = append(nCards, s[i], s[j], s[k])
						isNiu = true //有牛
						break for_i
					}
				}
			}
		}
	}
	return nCards, isNiu
}

// 5个数和3个数的差集[去掉第一个重复就可以]
func _5Cut3(s1, s2 []model.CardsData) []model.CardsData {
	lens1 := len(s1)
	lens2 := len(s2)
	if (lens1 != 5) || (lens2 > lens1) || (lens2 == 0) {
		return nil
	}
	pCards := make([]model.CardsData, len(s1))
	copy(pCards, s1) //必须复制一个，不然污染s1和源数据

	// fmt.Printf("s1: %v, s1: %p,  pCards: %v, s2:%v \n", s1, &s1, pCards, s2)
	for _, v := range s2 {
		for k2, v2 := range pCards {
			if v.Value == v2.Value {
				pCards = append(pCards[:k2], pCards[k2+1:]...) //相同一个就好
				break
			}
		}
	}
	// fmt.Printf("s1: %v, s1: %p,  pCards: %v, s2:%v \n", s1, &s1, pCards, s2)
	return pCards
}

// 检查2个牌点数是否牛几[niuNo: 对应ConfNIUData[牛几的]下标]
func CheckPCardsIsNiu(s []model.CardsData) (niuNo int) {
	totalPoint := 0
	for _, v := range s {
		totalPoint += Cards10JQKJOKER(v.Point)
		if totalPoint > 0 {
			niuNo = totalPoint % 10 //牛几
			if niuNo == 0 {
				niuNo = 10 //10的倍数是牛牛
			}
		}
	}
	if niuNo < 0 {
		niuNo = 0
	}
	return
}

/*
判断是否是炸弹[5张里面有4张一样,另一张随意]
*/
func IsBombCow(s []model.CardsData) bool {
	sameNum := 4 //要多少个相同的数
	lens := len(s)
	if sameNum > lens {
		return false
	}
	count := 1 //计数器
	for i := 1; i < lens; i++ {
		if s[i].Point > 0 && (s[i-1].Point == s[i].Point) {
			count++
		}
		if count == sameNum {
			return true
		}
	}
	return false
}

func IsFiveSmallCow(cards []model.CardsData) bool  { return CheckGTNiuNiu(cards, 6) } // 判断是否是五小牛[五张牌全部<5，且五张牌相加<10]
func IsFiveFlowerCow(cards []model.CardsData) bool { return CheckGTNiuNiu(cards, 5) } // 判断是否是五花牛[牌面J,Q,K,大王,小王任意组合]
func IsFourFlowerCow(cards []model.CardsData) bool { return CheckGTNiuNiu(cards, 4) } // 判断是否是四花牛[牌面10,J,Q,K,大王,小王任意组合，且组合中只能有一张10]

// ttype:[4:四花牛,5:五花牛,6:五小牛]
func CheckGTNiuNiu(s []model.CardsData, ttype int) bool {
	lens := len(s)
	if lens < 5 {
		return false //不够5张牌
	}
	count := 0      //计数器
	totalPoint := 0 //总点数
	for _, v := range s {
		if v.Suit <= 0 || v.Point <= 0 || v.Value <= 0 {
			return false //判断不低于0
		}
		switch ttype {
		case 4:
			if v.Point < 10 {
				return false //是否四花牛
			}
			if v.Cards10() {
				count++
			}
			if count > 1 {
				return false //只能有一个10
			}
		case 5:
			if v.Point < 11 {
				return false //是否五花牛
			}
		case 6:
			if v.Point >= 5 {
				return false //是否五小牛
			}
			totalPoint += v.Point //且五张牌相加<10
			if totalPoint >= 10 {
				return false //是否五小牛
			}
		default:
			return false
		}
	}
	return true
}

func IsStraightCow(cards []model.CardsData) bool { return IsStraightOrIsSameSuit(cards, false) } // 判断是否是顺子牛[返回值1: 判断顺子false, 返回值2: 判断同花色true]
func IsSameSuitCow(cards []model.CardsData) bool { return IsStraightOrIsSameSuit(cards, true) }  // 同花色[相同的花色]

// 是否顺子 or 同花色[参数2:同花色=true，顺子=false]
func IsStraightOrIsSameSuit(s []model.CardsData, same bool) bool {
	lens := len(s)
	if lens < 5 {
		return false //不够5张牌
	}
	if !same {
		model.SortByCardsPoint(s, true) //正序[判断顺子先排序]
	}
	for i := 1; i < lens; i++ {
		if s[i].Suit <= 0 || s[i].Point <= 0 || s[i].Value <= 0 {
			return false //判断不低于0
		}
		if s[i].Point >= POKER_JOKER_SMALL {
			return false //判断大小王
		}
		if same {
			//[同花]:是否同花色
			if s[i-1].Suit != s[i].Suit {
				return false
			}
		} else {
			//[顺子]:前一个数+1 = 后一个数
			if (s[i-1].Point + 1) != s[i].Point {
				return false
			}
		}
	}
	return true
}

// 相同手牌就对比大小+花色[大王>小王>黑K...方A] [s1:庄家手牌, s2:玩家手牌][返回：比较数]
func CompareCardsValue(s1, s2 []model.CardsData) (int, int) {
	model.SortByCardsPoint(s1, false) //倒序
	model.SortByCardsPoint(s2, false) //倒序
	if len(s1) != len(s2) {
		return 0, 0
	}
	for i := 0; i < len(s1); i++ {
		if s1[i].Value > s2[i].Value {
			return s1[i].Value, s2[i].Value
		}
		if s1[i].Value < s2[i].Value {
			return s1[i].Value, s2[i].Value
		}
		if s1[i].Value == s2[i].Value {
			if (i + 1) == len(s1) {
				return s1[i].Value, s2[i].Value
			}
			continue
		}
	}
	return 0, 0 //相等
}
